/*
	This file is part of PropExtractor.

	PropExtractor is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	PropExtractor is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with PropExtractor. If not, see <http://www.gnu.org/licenses/>.
*/

#include "PEIncludes.h"

#include "PEModel.h"
#include "PETools.h"
#include "PEFile.h"

namespace pe
{

	int PEModelRawInit(PEModelRaw* a_Model)
	{
		a_Model->name = NULL;

		a_Model->handle = NULL;

		a_Model->material = NULL;
		a_Model->material_total = 0;

		a_Model->mesh = NULL;
		a_Model->mesh_total = 0;

		a_Model->bone = NULL;
		a_Model->bone_total = 0;

		return PE_OK;
	}

	int PEBoneRawInit(PEBoneRaw* a_Bone)
	{
		a_Bone->name = NULL;

		a_Bone->parent = NULL;
		a_Bone->children = NULL;
		a_Bone->children_filled = 0;
		a_Bone->children_total = 0;
		a_Bone->localtransform = false;

		return PE_OK;
	}

	int PESkinBoneWeightsRawInit(PESkinRaw* a_SkinWeight)
	{
		a_SkinWeight->skinweight = NULL;
		a_SkinWeight->skinweight_total = 0;

		return PE_OK;
	}

	int PEMeshRawInit(PEMeshRaw* a_Mesh)
	{
		a_Mesh->name = NULL;

		a_Mesh->vertices = NULL;
		a_Mesh->texcoords = NULL;
		a_Mesh->normals = NULL;

		a_Mesh->material = NULL;

		a_Mesh->vertex_indices = NULL;
		a_Mesh->texcoord_indices = NULL;
		a_Mesh->normal_indices = NULL;

		a_Mesh->bonesused = NULL;
		a_Mesh->bonesused_total = 0;

		a_Mesh->skinboneweights = NULL;
		a_Mesh->skinboneweights_total = 0;

		return PE_OK;
	}

	int PEMaterialRawInit(PEMaterialRaw* a_Material)
	{
		a_Material->name = NULL;

		a_Material->submaterial = NULL;
		a_Material->submaterial_total = 0;
		a_Material->texture_diffuse = NULL;
		a_Material->texture_specular = NULL;
		a_Material->texture_total = 0;

		return PE_OK;
	}

	// ================================
	// WAVEFRONT
	// ================================

	int PEModelRawSaveToObj(PEModelRaw* a_Model, const wchar_t* a_FilePath)
	{
		wchar_t path[_MAX_PATH];
		size_t path_len;

		/*unsigned int dir_length = 0;
		char* lastslash = strrchr((char*)a_FilePath, '\\');
		if (!lastslash) 
		{ 
			lastslash = (char*)a_FilePath; 
			dir_length = strlen(a_FilePath);
		}
		else
		{
			lastslash++;
			dir_length = lastslash - a_FilePath;
		}

		unsigned int filename_length = 0;
		char* lastdot = strrchr((char*)a_FilePath, '.');
		if (!lastdot)
		{
			filename_length = strlen(lastslash + 1) + 1;
		}
		else
		{
			filename_length = lastdot - lastslash + 1;
		}
		char* filename = new char[filename_length];
		memset(filename, 0, filename_length);
		strncat(filename, lastslash, filename_length);*/
		

		// mtl

		GetPatternedPath(path, &path_len, "%workingdir%%name%.mtl", a_FilePath);

		wchar_t filename[_MAX_PATH];
		GetPatternedPath(filename, &path_len, "%name%", a_FilePath);

		FILE* write_mtl = _wfopen(path, L"w+");

		fprintf(write_mtl, "# Generated with PropExtractor "PE_VERSION_STRING"\n");
		fprintf(write_mtl, "\n");

		for (unsigned int i = 0; i < a_Model->material_total; i++)
		{
			if (a_Model->material[i].texture_total > 0)
			{
				fprintf(write_mtl, "newmtl %s\n", a_Model->material[i].name);
				fprintf(write_mtl, "illum 4\n");
				fprintf(write_mtl, "Kd 1.00 1.00 1.00\n");
				fprintf(write_mtl, "Ka 1.00 1.00 1.00\n");
				fprintf(write_mtl, "Ks 1.00 1.00 1.00\n");
				fprintf(write_mtl, "map_Ka uncompressed_%s\n", a_Model->material[i].texture_diffuse);
				fprintf(write_mtl, "map_Kd uncompressed_%s\n", a_Model->material[i].texture_diffuse);
				if (a_Model->material[i].texture_total > 1)
				{
					fprintf(write_mtl, "map_Ks %s.tga\n", a_Model->material[i].texture_specular);
				}
				fprintf(write_mtl, "\n");
			}

			if (a_Model->material[i].submaterial_total > 0)
			{
				fprintf(write_mtl, "# Submaterials: %i\n", a_Model->material[i].submaterial_total);
				fprintf(write_mtl, "\n");

				for (unsigned int j = 0; j < a_Model->material[i].submaterial_total; j++)
				{
					if (a_Model->material[i].submaterial[j].texture_total > 0)
					{
						fprintf(write_mtl, "newmtl %s\n", a_Model->material[i].submaterial[j].name);
						fprintf(write_mtl, "illum 4\n");
						fprintf(write_mtl, "Kd 1.00 1.00 1.00\n");
						fprintf(write_mtl, "Ka 1.00 1.00 1.00\n");
						fprintf(write_mtl, "Ks 1.00 1.00 1.00\n");
						fprintf(write_mtl, "map_Ka uncompressed_%s\n", a_Model->material[i].submaterial[j].texture_diffuse);
						fprintf(write_mtl, "map_Kd uncompressed_%s\n", a_Model->material[i].submaterial[j].texture_diffuse);
						if (a_Model->material[i].submaterial[j].texture_total > 1)
						{
							fprintf(write_mtl, "map_Ks uncompressed_%s\n", a_Model->material[i].submaterial[j].texture_specular);
						}
						fprintf(write_mtl, "\n");
					}
				}
			}

		}

		fclose(write_mtl);

		// obj

		GetPatternedPath(path, &path_len, "%workingdir%%name%.obj", a_FilePath, NULL, 0);

		FILE* write_obj = _wfopen(path, L"w+");

		fprintf(write_obj, "# Generated with PropExtractor "PE_VERSION_STRING"\n");
		fprintf(write_obj, "\n");
		fprintf(write_obj, "# Meshes: %i\n", a_Model->mesh_total);
		for (unsigned int j = 0; j < a_Model->mesh_total; j++)
		{
			fprintf(write_obj, "# %s\n", a_Model->mesh[j].name);
			fprintf(write_obj, "#   vertices: %i\n", a_Model->mesh[j].vertices->total);
			fprintf(write_obj, "#   uv's:     %i\n", a_Model->mesh[j].texcoords->total);
			fprintf(write_obj, "#   faces:    %i\n", a_Model->mesh[j].vertex_indices->total);
		}
		fprintf(write_obj, "\n");

		fprintf(write_obj, "mtllib %s.mtl\n", filename);

		// vertices

		for (unsigned int j = 0; j < a_Model->mesh_total; j++)
		{
			fprintf(write_obj, "\n");
			fprintf(write_obj, "# Mesh: %s - vertices: %i\n", 
				a_Model->mesh[j].name,
				a_Model->mesh[j].vertices->total
			);
			fprintf(write_obj, "\n");

			float* src = a_Model->mesh[j].vertices->position;
			for (unsigned int i = 0; i < a_Model->mesh[j].vertices->total; i++)
			{
				fprintf(
					write_obj, "v %f %f %f\n", 
					src[0], src[1], src[2]
				);

				src += 3;
			}
		}

		fprintf(write_obj, "\n");

		// texture coordinates

		for (unsigned int j = 0; j < a_Model->mesh_total; j++)
		{
			fprintf(write_obj, "\n");
			fprintf(write_obj, "# Mesh: %s - uv's: %i\n", 
				a_Model->mesh[j].name,
				a_Model->mesh[j].texcoords->total
			);
			fprintf(write_obj, "\n");

			float* src = a_Model->mesh[j].texcoords->uv;
			for (unsigned int i = 0; i < a_Model->mesh[j].texcoords->total; i++)
			{
				fprintf(
					write_obj, "vt %f %f\n", 
					src[0], src[1]
				);

				src += 2;
			}
		}

		fprintf(write_obj, "\n");

		// faces

		unsigned int vertex_offset = 0;
		unsigned int uv_offset = 0;

		for (unsigned int j = 0; j < a_Model->mesh_total; j++)
		{
			fprintf(write_obj, "# Mesh: %s - faces: %i\n", 
				a_Model->mesh[j].name,
				a_Model->mesh[j].vertex_indices->total
			);
			fprintf(write_obj, "\n");

			fprintf(write_obj, "usemtl %s\n", a_Model->mesh[j].material->name);
			fprintf(write_obj, "\n");

			unsigned int* src_v = a_Model->mesh[j].vertex_indices->index;
			unsigned int* src_uv = a_Model->mesh[j].texcoord_indices->index;
			for (unsigned int i = 0; i < a_Model->mesh[j].vertex_indices->total; i++)
			{
				fprintf(
					write_obj,
					"f %i/%i %i/%i %i/%i\n", 
					src_v[1] + vertex_offset + 1, src_uv[1] + uv_offset + 1,
					src_v[0] + vertex_offset + 1, src_uv[0] + uv_offset + 1,
					src_v[2] + vertex_offset + 1, src_uv[2] + uv_offset + 1
				);

				src_v += 3;
				src_uv += 3;
			}

			vertex_offset += a_Model->mesh[j].vertices->total;
			uv_offset += a_Model->mesh[j].texcoords->total;

			fprintf(write_obj, "\n");
		}

		fclose(write_obj);

		return PE_OK;
	}

	// ================================
	// COLLADA
	// ================================

	int AddBone(TiXmlElement* a_Target, PEBoneRaw* a_Bone, int a_ID)
	{
		TiXmlElement* node = new TiXmlElement("node");

		char* name;
		PE_WIDETOSTR(name, a_Bone->name);

		node->SetAttribute("id",   name);
		node->SetAttribute("name", name);
		node->SetAttribute("sid",  name);
		node->SetAttribute("type", "JOINT");

			TiXmlElement* translate = new TiXmlElement("translate");
			translate->SetAttribute("sid", "translate");
			char temp[256];
			sprintf(
				temp, 
				"%f %f %f", 
				a_Bone->inv_bind_position.x,
				a_Bone->inv_bind_position.y,
				a_Bone->inv_bind_position.z
			);
			translate->LinkEndChild(new TiXmlText(temp));
		node->LinkEndChild(translate);

		for (unsigned int i = 0; i < a_Bone->children_filled; i++)
		{
			a_ID += AddBone(node, a_Bone->children[i], a_ID);
		}

		a_Target->LinkEndChild(node);

		return a_ID + 1;
	}

	int PEModelRawSaveToCollada(PEModelRaw* a_Model, const char* a_FilePath)
	{
		TiXmlDocument doc;

		TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "UTF-8", "");
		doc.LinkEndChild(decl);

		TiXmlElement* collada = new TiXmlElement("COLLADA");
		collada->SetAttribute("xmlns", "http://www.collada.org/2005/11/COLLADASchema");
		collada->SetAttribute("version", "1.4.1");
		doc.LinkEndChild(collada);

		collada->LinkEndChild(new TiXmlComment("Header"));

		// header

		TiXmlElement* asset = new TiXmlElement("asset");
		{
			/*TiXmlElement* keywords = new TiXmlElement("keywords");
			keywords->LinkEndChild(new TiXmlText("bla bla"));
			asset->LinkEndChild(keywords);*/

			TiXmlElement* contributor = new TiXmlElement("contributor");
			asset->LinkEndChild(contributor);

			TiXmlElement* created = new TiXmlElement("created");
			created->LinkEndChild(new TiXmlText("2008-04-08T13:07:52-08:00"));
			asset->LinkEndChild(created);

			TiXmlElement* modified = new TiXmlElement("modified");
			modified->LinkEndChild(new TiXmlText("2008-04-08T13:07:52-08:00"));
			asset->LinkEndChild(modified);

			TiXmlElement* unit = new TiXmlElement("unit");
			unit->SetAttribute("name", "meter");
			unit->SetAttribute("meter", "1");
			asset->LinkEndChild(unit);

			TiXmlElement* up_axis = new TiXmlElement("up_axis");
			up_axis->LinkEndChild(new TiXmlText("Y_UP"));
			asset->LinkEndChild(up_axis);
		}
		
		// ================================
		// Materials
		// ================================

		collada->LinkEndChild(new TiXmlComment("Materials"));

		TiXmlElement* library_materials = new TiXmlElement("library_materials");
		TiXmlElement* library_images = new TiXmlElement("library_images");
		TiXmlElement* library_effects = new TiXmlElement("library_effects");

		PEMaterialRaw* src = a_Model->material;
		PEMaterialRaw* src_pop = NULL;
		unsigned int sub_count = 0;
		for (unsigned int i = 0; i < a_Model->material_total;)
		{
			// skip super materials

			if (sub_count == 0)
			{
				if (src->texture_total == 0) 
				{ 
					if (src->submaterial_total > 0)
					{
						src_pop = src;
						sub_count = src->submaterial_total;
						src = src->submaterial;
					}
					else
					{
						src++;
						i++;
					}

					continue; 
				}
			}
			else
			{
				if (src->texture_total == 0) 
				{
					if (--sub_count == 0)
					{
						src = src_pop + 1;
						sub_count = 0;
						i++;
					}
					else
					{
						src++;
					}

					continue;
				}
			}

			char temp[256];

			// material

			{
				TiXmlElement* material = new TiXmlElement("material");
				sprintf(temp, "%s-material", src->name);
				material->SetAttribute("id", temp);
				material->SetAttribute("name", temp);

					TiXmlElement* instance_effect = new TiXmlElement("instance_effect");
					sprintf(temp, "#%s-effect", src->name);
					instance_effect->SetAttribute("url", temp);

				material->LinkEndChild(instance_effect);

				library_materials->LinkEndChild(material);
			}

			// effect

			TiXmlElement* effect = new TiXmlElement("effect");
			sprintf(temp, "%s-effect", src->name);
			effect->SetAttribute("id", temp);
			
				TiXmlElement* profile_COMMON = new TiXmlElement("profile_COMMON");

					TiXmlElement* technique = new TiXmlElement("technique");
					technique->SetAttribute("sid", "common");

						TiXmlElement* blinn = new TiXmlElement("blinn");
		
						for (unsigned int j = 0; j < src->texture_total; j++)
						{
							// effect -> texture

							TiXmlElement* texture = new TiXmlElement("texture");

							char* read = NULL;
							if (j == 0) 
							{ 
								PE_WIDETOSTR(read, src->texture_diffuse);

									TiXmlElement* diffuse = new TiXmlElement("diffuse");
									diffuse->LinkEndChild(texture);
								blinn->LinkEndChild(diffuse);
							}
							else if (j == 1) 
							{ 
								PE_WIDETOSTR(read, src->texture_specular);

									TiXmlElement* specular = new TiXmlElement("specular");
									specular->LinkEndChild(texture);
								blinn->LinkEndChild(specular);
							}

							sprintf(temp, "%s-sampler", read);
							texture->SetAttribute("texture", temp);
							texture->SetAttribute("texcoord", "TEX0");

							// effect -> surface

							{
								TiXmlElement* newparam = new TiXmlElement("newparam");
								sprintf(temp, "%s-surface", read);
								newparam->SetAttribute("sid", temp);

									TiXmlElement* surface = new TiXmlElement("surface");
									surface->SetAttribute("type", "2D");

										TiXmlElement* init_from = new TiXmlElement("init_from");

											init_from->LinkEndChild(new TiXmlText(read));

										surface->LinkEndChild(init_from);

									newparam->LinkEndChild(surface);

								profile_COMMON->LinkEndChild(newparam);
							}

							// effect -> sampler

							{
								TiXmlElement* newparam = new TiXmlElement("newparam");
								sprintf(temp, "%s-sampler", read);
								newparam->SetAttribute("sid", temp);

									TiXmlElement* sampler2D = new TiXmlElement("sampler2D");

										TiXmlElement* source = new TiXmlElement("source");

											sprintf(temp, "%s-surface", read);
											source->LinkEndChild(new TiXmlText(temp));

										sampler2D->LinkEndChild(source);

									newparam->LinkEndChild(sampler2D);

								profile_COMMON->LinkEndChild(newparam);
							}

							// library_images -> texture

							{
								TiXmlElement* image = new TiXmlElement("image");
								image->SetAttribute("id", read);
								image->SetAttribute("name", read);

									TiXmlElement* init_from = new TiXmlElement("init_from");
									sprintf(temp, "uncompressed_%s", read);
									init_from->LinkEndChild(new TiXmlText(temp));

								image->LinkEndChild(init_from);

								library_images->LinkEndChild(image);
							}
						}

					technique->LinkEndChild(blinn);
				profile_COMMON->LinkEndChild(technique);
			effect->LinkEndChild(profile_COMMON);

			library_effects->LinkEndChild(effect);

			src++;

			if (sub_count > 0)
			{
				if (--sub_count == 0)
				{
					src = src_pop + 1;
					sub_count = 0;
					i++;
				}
			}
			else
			{
				i++;
			}
		}

		// meshes

		collada->LinkEndChild(new TiXmlComment("Meshes"));

		TiXmlElement* library_geometries = new TiXmlElement("library_geometries");

		// joints

		TiXmlElement* library_controllers = new TiXmlElement("library_controllers");

		// scene

		TiXmlElement* library_visual_scenes = new TiXmlElement("library_visual_scenes");

			TiXmlElement* visual_scene = new TiXmlElement("visual_scene");
			visual_scene->SetAttribute("id", "Scene");
			visual_scene->SetAttribute("name", "Scene");

				TiXmlElement* node_root = new TiXmlElement("node");

				char* name;
				PE_WIDETOSTR(name, a_Model->handle->name);

				node_root->SetAttribute("id", name);

		TiXmlElement* scene = new TiXmlElement("scene");

			TiXmlElement* instance_visual_scene = new TiXmlElement("instance_visual_scene");
			instance_visual_scene->SetAttribute("url", "#Scene");

		scene->LinkEndChild(instance_visual_scene);

		for (unsigned int i = 0; i < a_Model->mesh_total; i++)
		{
			char temp[512];

			// ================================
			// Mesh
			// ================================

			TiXmlElement* geometry = new TiXmlElement("geometry");
			char* name;
			PE_WIDETOSTR(name, a_Model->mesh[i].name);
			geometry->SetAttribute("id",   name);
			geometry->SetAttribute("name", name);

				TiXmlElement* mesh = new TiXmlElement("mesh");

					// vertices

					{
						TiXmlElement* source = new TiXmlElement("source");
						sprintf(temp, "%s-positions", a_Model->mesh[i].name);
						source->SetAttribute("id", temp);
						source->SetAttribute("name", "positions");

							TiXmlElement* float_array = new TiXmlElement("float_array");
							sprintf(temp, "%s-positions-array", a_Model->mesh[i].name);
							float_array->SetAttribute("id", temp);
							float_array->SetAttribute("count", a_Model->mesh[i].vertices->total * 3);

							// actual data

							char* float_values = new char[a_Model->mesh[i].vertices->total * 3 * 20];
							memset(float_values, 0, a_Model->mesh[i].vertices->total * 3 * 20);

							float* src_v = a_Model->mesh[i].vertices->position;
							for (unsigned int j = 0; j < a_Model->mesh[i].vertices->total; j++)
							{
								char dst[256];
								sprintf(dst, "%f %f %f ", src_v[0], src_v[1], src_v[2]);
								strcat(float_values, dst);

								src_v += 3;
							}

							float_array->LinkEndChild(new TiXmlText(float_values));
							delete float_values;

							source->LinkEndChild(float_array);

							// technique

							TiXmlElement* technique_common = new TiXmlElement("technique_common");

								TiXmlElement* accessor = new TiXmlElement("accessor");
								sprintf(temp, "#%s-positions-array", a_Model->mesh[i].name);
								accessor->SetAttribute("source", temp);
								accessor->SetAttribute("count", a_Model->mesh[i].vertices->total);
								accessor->SetAttribute("stride", 3);

									TiXmlElement* param_x = new TiXmlElement("param");
									param_x->SetAttribute("name", "X");
									param_x->SetAttribute("type", "float");
									accessor->LinkEndChild(param_x);

									TiXmlElement* param_y = new TiXmlElement("param");
									param_y->SetAttribute("name", "Y");
									param_y->SetAttribute("type", "float");
									accessor->LinkEndChild(param_y);

									TiXmlElement* param_z = new TiXmlElement("param");
									param_z->SetAttribute("name", "Z");
									param_z->SetAttribute("type", "float");
									accessor->LinkEndChild(param_z);

								technique_common->LinkEndChild(accessor);
							
							source->LinkEndChild(technique_common);

						mesh->LinkEndChild(source);
					}

					// texture coordinates

					{
						TiXmlElement* source = new TiXmlElement("source");
						sprintf(temp, "%s-map1", a_Model->mesh[i].name);
						source->SetAttribute("id", temp);
						source->SetAttribute("name", "map1");

							TiXmlElement* float_array = new TiXmlElement("float_array");
							sprintf(temp, "%s-map1-array", a_Model->mesh[i].name);
							float_array->SetAttribute("id", temp);
							float_array->SetAttribute("count", a_Model->mesh[i].texcoords->total * 2);

							// actual data

							char* float_values = new char[a_Model->mesh[i].texcoords->total * 2 * 20];
							memset(float_values, 0, a_Model->mesh[i].texcoords->total * 2 * 20);

							float* src_uv = a_Model->mesh[i].texcoords->uv;
							for (unsigned int j = 0; j < a_Model->mesh[i].texcoords->total; j++)
							{
								char dst[256];
								sprintf(dst, "%f %f ", src_uv[0], src_uv[1]);
								strcat(float_values, dst);

								src_uv += 2;
							}

							float_array->LinkEndChild(new TiXmlText(float_values));
							delete float_values;

							source->LinkEndChild(float_array);

							// technique

							TiXmlElement* technique_common = new TiXmlElement("technique_common");

								TiXmlElement* accessor = new TiXmlElement("accessor");
								sprintf(temp, "#%s-map1-array", a_Model->mesh[i].name);
								accessor->SetAttribute("source", temp);
								// NOTE: Blender crash when count is not correct
								accessor->SetAttribute("count", a_Model->mesh[i].texcoords->total);
								accessor->SetAttribute("stride", 2);

									TiXmlElement* param_s = new TiXmlElement("param");
									param_s->SetAttribute("name", "S");
									param_s->SetAttribute("type", "float");
									accessor->LinkEndChild(param_s);

									TiXmlElement* param_t = new TiXmlElement("param");
									param_t->SetAttribute("name", "T");
									param_t->SetAttribute("type", "float");
									accessor->LinkEndChild(param_t);

								technique_common->LinkEndChild(accessor);
							
							source->LinkEndChild(technique_common);

						mesh->LinkEndChild(source);
					}

					// faces

					{
						TiXmlElement* vertices = new TiXmlElement("vertices");
						sprintf(temp, "%s-vertices", a_Model->mesh[i].name);
						vertices->SetAttribute("id", temp);

							TiXmlElement* input = new TiXmlElement("input");
							input->SetAttribute("semantic", "POSITION");
							sprintf(temp, "#%s-positions", a_Model->mesh[i].name);
							input->SetAttribute("source", temp);

						vertices->LinkEndChild(input);

						mesh->LinkEndChild(vertices);
					}

					{
						TiXmlElement* triangles = new TiXmlElement("triangles");
						triangles->SetAttribute("count", a_Model->mesh[i].vertex_indices->total);
						sprintf(temp, "%s-symbol", a_Model->mesh[i].name);
						triangles->SetAttribute("material", temp);

							TiXmlElement* input_vertex = new TiXmlElement("input");
							input_vertex->SetAttribute("semantic", "VERTEX");
							sprintf(temp, "#%s-vertices", a_Model->mesh[i].name);
							input_vertex->SetAttribute("source", temp);
							input_vertex->SetAttribute("offset", 0);
							triangles->LinkEndChild(input_vertex);

							TiXmlElement* input_uv = new TiXmlElement("input");
							input_uv->SetAttribute("semantic", "TEXCOORD");
							sprintf(temp, "#%s-map1", a_Model->mesh[i].name);
							input_uv->SetAttribute("source", temp);
							input_uv->SetAttribute("offset", 1);
							input_uv->SetAttribute("set", 0);
							triangles->LinkEndChild(input_uv);

							char* index_values = new char[a_Model->mesh[i].vertex_indices->total * 3 * 20];
							memset(index_values, 0, a_Model->mesh[i].vertex_indices->total * 3 * 20);

							unsigned int* index_v = a_Model->mesh[i].vertex_indices->index;
							unsigned int* index_uv = a_Model->mesh[i].texcoord_indices->index;
							for (unsigned int j = 0; j < a_Model->mesh[i].vertex_indices->total; j++)
							{
								char dst[256];
								// NOTE: Blender crash when encountering invalid index
								sprintf(dst, "%i %i %i %i %i %i ", 
									index_v[1], index_uv[1], 
									index_v[0], index_uv[0], 
									index_v[2], index_uv[2]
								);

								if (a_Model->mesh[i].texcoords->total)
								/*sprintf(dst, "%i %i %i ", 
									index_v[1], 
									index_v[0], 
									index_v[2]
								);*/
								strcat(index_values, dst);

								index_v += 3;
								index_uv += 3;
							}

							TiXmlElement* p = new TiXmlElement("p");
							p->LinkEndChild(new TiXmlText(index_values));
							delete index_values;
							triangles->LinkEndChild(p);

						mesh->LinkEndChild(triangles);
					}

			geometry->LinkEndChild(mesh);

			library_geometries->LinkEndChild(geometry);

			// ================================
			// Joints
			// ================================

			{
				unsigned int bones_total = a_Model->bone_total;
				// NOTE: Blender fails to import skeleton if bindpose count != name count
				unsigned int bindpose_total = a_Model->bone_total;
				PEBoneRaw* bindpose_bones = a_Model->bone;
				PEBoneRaw* bones = a_Model->bone;

				TiXmlElement* controller = new TiXmlElement("controller");
				sprintf(temp, "%s-skin", a_Model->mesh[i].name);
				controller->SetAttribute("id", temp);
				sprintf(temp, "SkinCluster%i", i);
				controller->SetAttribute("name", temp);

					TiXmlElement* skin = new TiXmlElement("skin");
					sprintf(temp, "#%s", a_Model->mesh[i].name);
					skin->SetAttribute("source", temp);

						TiXmlElement* bind_shape_matrix = new TiXmlElement("bind_shape_matrix");
						bind_shape_matrix->LinkEndChild(new TiXmlText("1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"));
					skin->LinkEndChild(bind_shape_matrix);

					// names

					{
						TiXmlElement* source = new TiXmlElement("source");
						sprintf(temp, "%s-skin-joints", a_Model->mesh[i].name);
						source->SetAttribute("id", temp);

							// values

							TiXmlElement* Name_array = new TiXmlElement("Name_array");
							sprintf(temp, "%s-skin-joints-array", a_Model->mesh[i].name);
							Name_array->SetAttribute("id", temp);

							Name_array->SetAttribute("count", bones_total);
							
							char* name_values = new char[bones_total * 32];
							memset(name_values, 0, bones_total * 32);
							for (unsigned int j = 0; j < bones_total; j++)
							{
								char* name;
								PE_WIDETOSTR(name, bones[j].name);

								strcat(name_values, name);
								strcat(name_values, " ");
							}

							Name_array->LinkEndChild(new TiXmlText(name_values));

							delete name_values;

							// technique

							TiXmlElement* technique_common = new TiXmlElement("technique_common");

								TiXmlElement* accessor = new TiXmlElement("accessor");
								sprintf(temp, "#%s-skin-joints-array", a_Model->mesh[i].name);
								accessor->SetAttribute("source", temp);
								accessor->SetAttribute("count", bones_total);
								accessor->SetAttribute("stride", 1);

									TiXmlElement* param = new TiXmlElement("param");
									param->SetAttribute("name", "JOINT");
									param->SetAttribute("type", "Name");

								accessor->LinkEndChild(param);
							technique_common->LinkEndChild(accessor);

						source->LinkEndChild(Name_array);
						source->LinkEndChild(technique_common);

						skin->LinkEndChild(source);
					}

					// bind poses

					{
						TiXmlElement* source = new TiXmlElement("source");
						sprintf(temp, "%s-skin-bindposes", a_Model->mesh[i].name);
						source->SetAttribute("id", temp);

							TiXmlElement* float_array = new TiXmlElement("float_array");

							char* bind_values = new char[bindpose_total * 16 * 16];
							memset(bind_values, 0, bindpose_total * 16 * 16);
							for (unsigned int j = 0; j < bindpose_total; j++)
							{
								char temp2[256];

								if (bindpose_bones[j].localtransform)
								{
									/*sprintf(
										temp2, 
										"1 0 0 %f 0 1 0 %f 0 0 1 %f 0 0 0 1 ", 
										bindpose_bones[j].inv_bind_position.x,
										bindpose_bones[j].inv_bind_position.y,
										bindpose_bones[j].inv_bind_position.z
									);*/

									float tempmat[16] = {
										bindpose_bones[j].inv_bind_matrix[0].x, 
										bindpose_bones[j].inv_bind_matrix[0].y, 
										bindpose_bones[j].inv_bind_matrix[0].z,
										bindpose_bones[j].inv_bind_position.x,

										bindpose_bones[j].inv_bind_matrix[1].x, 
										bindpose_bones[j].inv_bind_matrix[1].y, 
										bindpose_bones[j].inv_bind_matrix[1].z,
										bindpose_bones[j].inv_bind_position.y,

										bindpose_bones[j].inv_bind_matrix[2].x, 
										bindpose_bones[j].inv_bind_matrix[2].y, 
										bindpose_bones[j].inv_bind_matrix[2].z,
										bindpose_bones[j].inv_bind_position.z,

										bindpose_bones[j].inv_bind_matrix[3].x, 
										bindpose_bones[j].inv_bind_matrix[3].y, 
										bindpose_bones[j].inv_bind_matrix[3].z,
										bindpose_bones[j].inv_bind_matrix[3].w
									};

									sprintf(
										temp2, 
										"%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f ", 
										bindpose_bones[j].inv_bind_matrix[0].x, 
										bindpose_bones[j].inv_bind_matrix[0].y, 
										bindpose_bones[j].inv_bind_matrix[0].z,
										bindpose_bones[j].inv_bind_position.x,

										bindpose_bones[j].inv_bind_matrix[1].x, 
										bindpose_bones[j].inv_bind_matrix[1].y, 
										bindpose_bones[j].inv_bind_matrix[1].z,
										bindpose_bones[j].inv_bind_position.y,

										bindpose_bones[j].inv_bind_matrix[2].x, 
										bindpose_bones[j].inv_bind_matrix[2].y, 
										bindpose_bones[j].inv_bind_matrix[2].z,
										bindpose_bones[j].inv_bind_position.z,

										bindpose_bones[j].inv_bind_matrix[3].x, 
										bindpose_bones[j].inv_bind_matrix[3].y, 
										bindpose_bones[j].inv_bind_matrix[3].z,
										bindpose_bones[j].inv_bind_matrix[3].w
									);

									int bleh = 0;
								}
								else
								{
									sprintf(
										temp2, 
										"1.0 0 0 0 0 1.0 0 0 0 0 1.0 0 0 0 0 1.0 "
									);
								}

								strcat(bind_values, temp2);
							}

							sprintf(temp, "%s-skin-bindposes-array", a_Model->mesh[i].name);
							float_array->SetAttribute("id", temp);
							float_array->SetAttribute("count", bindpose_total * 16);
							float_array->LinkEndChild(new TiXmlText(bind_values));

							delete bind_values;

						source->LinkEndChild(float_array);

							TiXmlElement* technique_common = new TiXmlElement("technique_common");
								TiXmlElement* accessor = new TiXmlElement("accessor");
								sprintf(temp, "#%s-skin-bindposes-array", a_Model->mesh[i].name);
								accessor->SetAttribute("source", temp);
								accessor->SetAttribute("count", bindpose_total);
								accessor->SetAttribute("stride", 16);

									TiXmlElement* param = new TiXmlElement("param");
									param->SetAttribute("name", "TRANSFORM");
									param->SetAttribute("type", "float4x4");

								accessor->LinkEndChild(param);
							technique_common->LinkEndChild(accessor);

						source->LinkEndChild(technique_common);

						skin->LinkEndChild(source);
					}

					// vertex weight data

					{
						PE_DEBUG("Writing vertex weights for mesh '%s'", a_Model->mesh[i].name);

						TiXmlElement* source = new TiXmlElement("source");
						sprintf(temp, "%s-skin-weights", a_Model->mesh[i].name);
						source->SetAttribute("id", temp);

							TiXmlElement* float_array = new TiXmlElement("float_array");
							sprintf(temp, "%s-skin-weights-array", a_Model->mesh[i].name);
							float_array->SetAttribute("id", temp);
							float_array->SetAttribute("count", a_Model->mesh[i].skinboneweights->skinweight_total);

							char* weight_data = new char[a_Model->mesh[i].skinboneweights->skinweight_total * 32];
							memset(weight_data, 0, a_Model->mesh[i].skinboneweights->skinweight_total * 32);

							for (unsigned int j = 0; j < a_Model->mesh[i].skinboneweights->skinweight_total; j++)
							{
								sprintf(temp, "%f ", a_Model->mesh[i].skinboneweights->skinweight[j].weight);
								strcat(weight_data, temp);
							}

							float_array->LinkEndChild(new TiXmlText(weight_data));

							delete weight_data;

						source->LinkEndChild(float_array);

							TiXmlElement* technique_common = new TiXmlElement("technique_common");

								TiXmlElement* accessor = new TiXmlElement("accessor");
								sprintf(temp, "#%s-skin-weights-array", a_Model->mesh[i].name);
								accessor->SetAttribute("source", temp);
								accessor->SetAttribute("count", a_Model->mesh[i].skinboneweights->skinweight_total);
								accessor->SetAttribute("stride", "1");

									TiXmlElement* param = new TiXmlElement("param");
									param->SetAttribute("name", "WEIGHT");
									param->SetAttribute("type", "float");
								
								accessor->LinkEndChild(param);

							technique_common->LinkEndChild(accessor);

						source->LinkEndChild(technique_common);

						skin->LinkEndChild(source);
					}

					// joints

					{
						TiXmlElement* joints = new TiXmlElement("joints");

							TiXmlElement* input1 = new TiXmlElement("input");
							input1->SetAttribute("semantic", "JOINT");
							sprintf(temp, "#%s-skin-joints", a_Model->mesh[i].name);
							input1->SetAttribute("source", temp);
							//input1->SetAttribute("offset", 0);

						joints->LinkEndChild(input1);

							TiXmlElement* input2 = new TiXmlElement("input");
							input2->SetAttribute("semantic", "INV_BIND_MATRIX");
							sprintf(temp, "#%s-skin-bindposes", a_Model->mesh[i].name);
							input2->SetAttribute("source", temp);
							//input2->SetAttribute("offset", 1);
						
						joints->LinkEndChild(input2);

						skin->LinkEndChild(joints);
					}

					// vertex weights

					{
						TiXmlElement* vertex_weights = new TiXmlElement("vertex_weights");
						vertex_weights->SetAttribute("count", a_Model->mesh[i].vertices->total);

						// names

							TiXmlElement* input_joint = new TiXmlElement("input");
							input_joint->SetAttribute("semantic", "JOINT");
							sprintf(temp, "#%s-skin-joints", a_Model->mesh[i].name);
							input_joint->SetAttribute("source", temp);
							input_joint->SetAttribute("offset", 0);

						vertex_weights->LinkEndChild(input_joint);

						// weights

							TiXmlElement* input_weight = new TiXmlElement("input");
							input_weight->SetAttribute("semantic", "WEIGHT");
							sprintf(temp, "#%s-skin-weights", a_Model->mesh[i].name);
							input_weight->SetAttribute("source", temp);
							input_weight->SetAttribute("offset", 1);

						vertex_weights->LinkEndChild(input_weight);

						// bones per vertex

							TiXmlElement* v = new TiXmlElement("v");

							char* assign_data = new char[a_Model->mesh[i].vertices->total * a_Model->bone_total * 16 * 2];
							memset(assign_data, 0, a_Model->mesh[i].vertices->total * a_Model->bone_total * 16 * 2);

							TiXmlElement* vcount = new TiXmlElement("vcount");
							char* influence_data = new char[a_Model->mesh[i].skinboneweights->skinweight_total * 16];
							memset(influence_data, 0, a_Model->mesh[i].skinboneweights->skinweight_total * 16);

							char temp_v[512];
							char temp_vcount[512];

							unsigned int j = 0;
							unsigned int prev = -1;
							unsigned int prev_count = 0;
							unsigned int total_vcount = 0;
							PESkinWeightRaw* src_skin = a_Model->mesh[i].skinboneweights->skinweight;

							while (1)
							{
								if (src_skin->vertex_id != prev && prev != 0xFFFFFFFF)
								{
									if (prev_count > 0)
									{
										total_vcount += prev_count;

										sprintf(temp_vcount, "%i ", prev_count);
										strcat(influence_data, temp_vcount);
										prev_count = 0;
									}
								}
								
								sprintf(temp_v, "%i %i ", src_skin->bone_id, j);
								strcat(assign_data, temp_v);
								prev_count++;
								prev = src_skin->vertex_id;

								if (++j == a_Model->mesh[i].skinboneweights->skinweight_total) 
								{ 
									break; 
								}

								src_skin++;
							} 

							if (prev_count > 0)
							{
								total_vcount += prev_count;

								sprintf(temp_vcount, "%i ", prev_count);
								strcat(influence_data, temp_vcount);
							}

							vcount->LinkEndChild(new TiXmlText(influence_data));
							delete influence_data;

						vertex_weights->LinkEndChild(vcount);

						// vertices

							

							/*for (unsigned int j = 0; j < a_Model->mesh[i].vertices->total; j++)
							{
								for (unsigned int k = 0; k < a_Model->bone_total; k++)
								{
									//sprintf(temp, "%i %i ", k, j * a_Model->bone_total + k);
									sprintf(temp, "%i %i ", k, 0);
									strcat(assign_data, temp);
								}
							}*/

							v->LinkEndChild(new TiXmlText(assign_data));

							delete assign_data;

						vertex_weights->LinkEndChild(v);

						skin->LinkEndChild(vertex_weights);
					}

				controller->LinkEndChild(skin);

				library_controllers->LinkEndChild(controller);
			}

			// ================================
			// Scene node
			// ================================

			bool bones = true;

			{
				TiXmlElement* node = new TiXmlElement("node");

				char* name;
				PE_WIDETOSTR(name, a_Model->mesh[i].name);

				sprintf(temp, "%s-node", name);
				node->SetAttribute("id", temp);
				node->SetAttribute("name", name);

				TiXmlElement* bind_target = NULL;

				if (bones)
				{
					TiXmlElement* instance_controller = new TiXmlElement("instance_controller");
					sprintf(temp, "#%s-skin", a_Model->mesh[i].name);
					instance_controller->SetAttribute("url", temp);

						TiXmlElement* skeleton = new TiXmlElement("skeleton");
						skeleton->LinkEndChild(new TiXmlText("#skeleton-root"));

					instance_controller->LinkEndChild(skeleton);

					bind_target = instance_controller;
				}
				else
				{
					TiXmlElement* instance_geometry = new TiXmlElement("instance_geometry");
					sprintf(temp, "#%s", a_Model->mesh[i].name);
					instance_geometry->SetAttribute("url", temp);

					bind_target = instance_geometry;
				}

					// material

					TiXmlElement* bind_material = new TiXmlElement("bind_material");

						TiXmlElement* technique_common = new TiXmlElement("technique_common");

							TiXmlElement* instance_material = new TiXmlElement("instance_material");
							sprintf(temp, "%s-symbol", a_Model->mesh[i].name);
							instance_material->SetAttribute("symbol", temp);
							sprintf(temp, "#%s-material", a_Model->mesh[i].material->name);
							instance_material->SetAttribute("target", temp);

							for (unsigned int j = 0; j < a_Model->mesh[i].material->texture_total; j++)
							{
								TiXmlElement* bind_vertex_input = new TiXmlElement("bind_vertex_input");

								if (j == 0 && a_Model->mesh[i].material->texture_diffuse)
								{
									bind_vertex_input->SetAttribute("semantic", "TEX0");
								}
								else if (j == 1 && a_Model->mesh[i].material->texture_specular)
								{
									bind_vertex_input->SetAttribute("semantic", "TEX1");
								}

								bind_vertex_input->SetAttribute("input_semantic", "TEXCOORD");
								bind_vertex_input->SetAttribute("input_set", "0");

								instance_material->LinkEndChild(bind_vertex_input);
							}

						technique_common->LinkEndChild(instance_material);
					bind_material->LinkEndChild(technique_common);
				bind_target->LinkEndChild(bind_material);

				node->LinkEndChild(bind_target);

				node_root->LinkEndChild(node);
				//visual_scene->LinkEndChild(node);
			}
			
		}

		// joint nodes

		TiXmlElement* root_node = new TiXmlElement("node");
		root_node->SetAttribute("id", "skeleton-root");
		root_node->SetAttribute("name", "skeleton-root");
		root_node->SetAttribute("sid", "skeleton-root");
		root_node->SetAttribute("type", "JOINT");

		int joint_node_result = 0;
		for (unsigned int j = 0; j < a_Model->bone_total; j++)
		{
			if (a_Model->bone[j].parent == NULL)
			{
				joint_node_result += AddBone(root_node, &a_Model->bone[j], joint_node_result);
			}
		}

		visual_scene->LinkEndChild(root_node);

		collada->LinkEndChild(asset);
		collada->LinkEndChild(library_images);
		collada->LinkEndChild(library_effects);
		collada->LinkEndChild(library_materials);
		collada->LinkEndChild(library_geometries);
		collada->LinkEndChild(library_controllers);

				visual_scene->LinkEndChild(node_root);
			library_visual_scenes->LinkEndChild(visual_scene);
		collada->LinkEndChild(library_visual_scenes);
		collada->LinkEndChild(scene);

		doc.SaveFile(a_FilePath);

		return PE_OK;
	}

	int PEModelRawSaveToFile(PEModelRaw* a_Model, const char* a_FilePath)
	{
		return PE_OK;
	}

	int PEMaterialRawDelete(PEMaterialRaw* a_Material)
	{
		// submaterials

		for (unsigned int j = 0; j < a_Material->submaterial_total; j++)
		{
			PEMaterialRawDelete(&a_Material->submaterial[j]);
		}

		// textures

		if (a_Material->texture_total > 1)
		{
			delete a_Material->texture_specular;
		}
		if (a_Material->texture_total > 0)
		{
			delete a_Material->texture_diffuse;
		}

		// name

		delete a_Material->name;

		return PE_OK;
	}

	int PEMeshRawDelete(PEMeshRaw* a_Mesh)
	{
		// data

		if (a_Mesh->vertices)
		{
			delete a_Mesh->vertices->position;
			delete a_Mesh->vertices;
		}
		if (a_Mesh->texcoords)
		{
			delete a_Mesh->texcoords->uv;
			delete a_Mesh->texcoords;
		}
		if (a_Mesh->normals)
		{
			delete a_Mesh->normals->normal;
			delete a_Mesh->normals;
		}

		// indices

		if (a_Mesh->vertex_indices) 
		{ 
			delete a_Mesh->vertex_indices->index;
			delete a_Mesh->vertex_indices;
		}
		if (a_Mesh->texcoord_indices) 
		{ 
			delete a_Mesh->texcoord_indices->index;
			delete a_Mesh->texcoord_indices;
		}
		if (a_Mesh->normal_indices) 
		{ 
			delete a_Mesh->normal_indices->index;
			delete a_Mesh->normal_indices;
		}

		// name

		delete a_Mesh->name;

		return PE_OK;
	}

	int PEModelRawDelete(PEModelRaw* a_Model)
	{
		// materials

		for (unsigned int i = 0; i < a_Model->material_total; i++)
		{
			PEMaterialRawDelete(&a_Model->material[i]);
		}

		// meshes

		for (unsigned int i = 0; i < a_Model->mesh_total; i++)
		{
			PEMeshRawDelete(&a_Model->mesh[i]);
		}

		// name

		delete a_Model->name;

		return PE_OK;
	}

}; // namespace pe